package org.chl.sqlsession.sqltemplate.core;

import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;

public class Context {

	private static Logger logger = LoggerFactory.getLogger(Context.class);

	static {
		OgnlRuntime.setPropertyAccessor(HashMap.class, new ContextAccessor());
	}

	public static final String BINDING_DATA = "_data";

	private Map<String, Object> binding;

	private StringBuilder sql = new StringBuilder();

	private List<Object> parameter;

	private int uniqueNumber = 0;

	public Context(Object data) {
		binding = new HashMap<String, Object>();
		parameter = new ArrayList<Object>();
		if (!(data instanceof Map)) {
			try {
				data = object2Nap(data);
			} catch (Exception e) {
				logger.error("object to mao fail {}", e);
				throw new RuntimeException(e);
			}
		}
		binding.put(BINDING_DATA, data);
	}

	private Map<String, Object> object2Nap(Object obj) throws Exception {
		if (Objects.isNull(obj)) {
			return null;
		}
		Map<String, Object> map = new HashMap<>();
		BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor property : propertyDescriptors) {
			String name = property.getName();
			if (name.compareToIgnoreCase("class") == 0) {
				continue;
			}
			Method readMethod = property.getReadMethod();
			Object value = Objects.nonNull(readMethod) ? readMethod.invoke(obj) : null;
			map.put(name, value);
		}
		return map;
	}

	public void bind(String key, Object value) {
		binding.put(key, value);
	}

	public void appendSql(String sqlFragement) {
		sql.append(sqlFragement).append(" ");
	}

	public Map<String, Object> getBinding() {
		return this.binding;
	}

	public List<Object> getParameter() {
		return this.parameter;
	}

	public void addParameter(Object parameter) {
		this.parameter.add(parameter);
	}

	public String getSql() {
		return sql.toString();
	}

	public void setSql(String sql) {
		this.sql = new StringBuilder(sql);
	}

	public int getUniqueNumber() {
		return ++uniqueNumber;
	}

	static class ContextAccessor implements PropertyAccessor {

		public Object getProperty(Map context, Object target, Object name)
				throws OgnlException {
			Map map = (Map) target;

			Object result = map.get(name);
			if (result != null) {
				return result;
			}

			Object parameterObject = map.get(BINDING_DATA);
			if (parameterObject instanceof Map) {
				return ((Map) parameterObject).get(name);
			}

			return null;
		}

		public void setProperty(Map context, Object target, Object name,
				Object value) throws OgnlException {
			Map map = (Map) target;
			map.put(name, value);
		}

		public String getSourceAccessor(OgnlContext arg0, Object arg1,
				Object arg2) {
			// TODO Auto-generated method stub
			return null;
		}

		public String getSourceSetter(OgnlContext arg0, Object arg1, Object arg2) {
			// TODO Auto-generated method stub
			return null;
		}

	}

}
